
import {
    Block,
    Hash,
    Transaction,
    TransactionReceipt,
    createTestClient,
    publicActions,
    walletActions,
    webSocket,
  } from "viem";
  import { hardhat } from "viem/chains";
  import { TransactionWithFunction } from "./utils/scaffold-eth/block";
  import { GenericContractsDeclaration } from "./utils/scaffold-eth/contract";
import { Abi, decodeFunctionData, getAbiItem } from "viem";
//import contractData from "./contracts/deployedContracts";
  //import { decodeTransactionData } from "./utils/scaffold-eth/decodeTxData";

  const contractData = {
    31337: {
      BuyMeACoffee: {
        address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
        abi: [
          {
            inputs: [],
            stateMutability: "nonpayable",
            type: "constructor",
          },
          {
            inputs: [],
            name: "InsufficientFunds",
            type: "error",
          },
          {
            inputs: [
              {
                internalType: "string",
                name: "message",
                type: "string",
              },
            ],
            name: "InvalidArguments",
            type: "error",
          },
          {
            inputs: [],
            name: "OnlyOwner",
            type: "error",
          },
          {
            anonymous: false,
            inputs: [
              {
                indexed: true,
                internalType: "address",
                name: "buyer",
                type: "address",
              },
              {
                indexed: false,
                internalType: "uint256",
                name: "price",
                type: "uint256",
              },
            ],
            name: "BuyMeACoffeeEvent",
            type: "event",
          },
          {
            anonymous: false,
            inputs: [
              {
                indexed: true,
                internalType: "address",
                name: "userAddress",
                type: "address",
              },
              {
                indexed: false,
                internalType: "uint256",
                name: "time",
                type: "uint256",
              },
              {
                indexed: false,
                internalType: "uint256",
                name: "numCoffees",
                type: "uint256",
              },
              {
                indexed: false,
                internalType: "string",
                name: "userName",
                type: "string",
              },
              {
                indexed: false,
                internalType: "string",
                name: "twitterHandle",
                type: "string",
              },
              {
                indexed: false,
                internalType: "string",
                name: "message",
                type: "string",
              },
            ],
            name: "NewMemo",
            type: "event",
          },
          {
            inputs: [
              {
                internalType: "uint256",
                name: "numCoffees",
                type: "uint256",
              },
              {
                internalType: "string",
                name: "userName",
                type: "string",
              },
              {
                internalType: "string",
                name: "twitterHandle",
                type: "string",
              },
              {
                internalType: "string",
                name: "message",
                type: "string",
              },
            ],
            name: "buyCoffee",
            outputs: [],
            stateMutability: "payable",
            type: "function",
          },
          {
            inputs: [],
            name: "getMemos",
            outputs: [
              {
                components: [
                  {
                    internalType: "uint256",
                    name: "numCoffees",
                    type: "uint256",
                  },
                  {
                    internalType: "string",
                    name: "userName",
                    type: "string",
                  },
                  {
                    internalType: "string",
                    name: "twitterHandle",
                    type: "string",
                  },
                  {
                    internalType: "string",
                    name: "message",
                    type: "string",
                  },
                  {
                    internalType: "uint256",
                    name: "time",
                    type: "uint256",
                  },
                  {
                    internalType: "address",
                    name: "userAddress",
                    type: "address",
                  },
                ],
                internalType: "struct Memo[]",
                name: "",
                type: "tuple[]",
              },
            ],
            stateMutability: "view",
            type: "function",
          },
          {
            inputs: [
              {
                internalType: "uint256",
                name: "",
                type: "uint256",
              },
            ],
            name: "memos",
            outputs: [
              {
                internalType: "uint256",
                name: "numCoffees",
                type: "uint256",
              },
              {
                internalType: "string",
                name: "userName",
                type: "string",
              },
              {
                internalType: "string",
                name: "twitterHandle",
                type: "string",
              },
              {
                internalType: "string",
                name: "message",
                type: "string",
              },
              {
                internalType: "uint256",
                name: "time",
                type: "uint256",
              },
              {
                internalType: "address",
                name: "userAddress",
                type: "address",
              },
            ],
            stateMutability: "view",
            type: "function",
          },
          {
            inputs: [
              {
                internalType: "uint256",
                name: "index",
                type: "uint256",
              },
              {
                internalType: "string",
                name: "message",
                type: "string",
              },
            ],
            name: "modifyMemoMessage",
            outputs: [],
            stateMutability: "nonpayable",
            type: "function",
          },
          {
            inputs: [],
            name: "owner",
            outputs: [
              {
                internalType: "address payable",
                name: "",
                type: "address",
              },
            ],
            stateMutability: "view",
            type: "function",
          },
          {
            inputs: [],
            name: "price",
            outputs: [
              {
                internalType: "uint256",
                name: "",
                type: "uint256",
              },
            ],
            stateMutability: "view",
            type: "function",
          },
          {
            inputs: [
              {
                internalType: "uint256",
                name: "index",
                type: "uint256",
              },
            ],
            name: "removeMemo",
            outputs: [],
            stateMutability: "nonpayable",
            type: "function",
          },
          {
            inputs: [],
            name: "withdrawTips",
            outputs: [],
            stateMutability: "nonpayable",
            type: "function",
          },
          {
            stateMutability: "payable",
            type: "receive",
          },
        ],
        inheritedFunctions: {},
      },
      YourContract: {
        address: "0x5FbDB2315678afecb367f032d93F642f64180aa3",
        abi: [
          {
            inputs: [
              {
                internalType: "address",
                name: "_owner",
                type: "address",
              },
            ],
            stateMutability: "nonpayable",
            type: "constructor",
          },
          {
            anonymous: false,
            inputs: [
              {
                indexed: true,
                internalType: "address",
                name: "greetingSetter",
                type: "address",
              },
              {
                indexed: false,
                internalType: "string",
                name: "newGreeting",
                type: "string",
              },
              {
                indexed: false,
                internalType: "bool",
                name: "premium",
                type: "bool",
              },
              {
                indexed: false,
                internalType: "uint256",
                name: "value",
                type: "uint256",
              },
            ],
            name: "GreetingChange",
            type: "event",
          },
          {
            inputs: [],
            name: "greeting",
            outputs: [
              {
                internalType: "string",
                name: "",
                type: "string",
              },
            ],
            stateMutability: "view",
            type: "function",
          },
          {
            inputs: [],
            name: "owner",
            outputs: [
              {
                internalType: "address",
                name: "",
                type: "address",
              },
            ],
            stateMutability: "view",
            type: "function",
          },
          {
            inputs: [],
            name: "premium",
            outputs: [
              {
                internalType: "bool",
                name: "",
                type: "bool",
              },
            ],
            stateMutability: "view",
            type: "function",
          },
          {
            inputs: [
              {
                internalType: "string",
                name: "_newGreeting",
                type: "string",
              },
            ],
            name: "setGreeting",
            outputs: [],
            stateMutability: "payable",
            type: "function",
          },
          {
            inputs: [],
            name: "totalCounter",
            outputs: [
              {
                internalType: "uint256",
                name: "",
                type: "uint256",
              },
            ],
            stateMutability: "view",
            type: "function",
          },
          {
            inputs: [
              {
                internalType: "address",
                name: "",
                type: "address",
              },
            ],
            name: "userGreetingCounter",
            outputs: [
              {
                internalType: "uint256",
                name: "",
                type: "uint256",
              },
            ],
            stateMutability: "view",
            type: "function",
          },
          {
            inputs: [],
            name: "withdraw",
            outputs: [],
            stateMutability: "nonpayable",
            type: "function",
          },
          {
            stateMutability: "payable",
            type: "receive",
          },
        ],
        inheritedFunctions: {},
      },
    },
  } as const;

  const BLOCKS_PER_PAGE = 20;

export const testClient = createTestClient({
  chain: hardhat,
  mode: "hardhat",
  transport: webSocket("ws://127.0.0.1:8545"),
})
  .extend(publicActions)
  .extend(walletActions);

  type ContractsInterfaces = Record<string, Abi>;
  type TransactionType = TransactionWithFunction | null;
  
  const deployedContracts = contractData as GenericContractsDeclaration | null;
  const chainMetaData = deployedContracts?.[hardhat.id];
  const interfaces = chainMetaData
    ? Object.entries(chainMetaData).reduce((finalInterfacesObj, [contractName, contract]) => {
        finalInterfacesObj[contractName] = contract.abi;
        return finalInterfacesObj;
      }, {} as ContractsInterfaces)
    : {};  

  const decodeTransactionData = (tx: TransactionWithFunction) => {
    if (tx.input.length >= 10 && !tx.input.startsWith("0x60e06040")) {
      for (const [, contractAbi] of Object.entries(interfaces)) {
        try {
          const { functionName, args } = decodeFunctionData({
            abi: contractAbi,
            data: tx.input,
          });
          tx.functionName = functionName;
          tx.functionArgs = args as any[];
          tx.functionArgNames = getAbiItem({ abi: contractAbi, name: functionName }).inputs.map(
            (input: any) => input.name,
          );
          tx.functionArgTypes = getAbiItem({ abi: contractAbi, name: functionName }).inputs.map(
            (input: any) => input.type,
          );
  
          break;
        } catch (e) {
          console.error(`Parsing failed: ${e}`);
        }
      }
    }
    return tx;
  };    

    try {
      const blockNumber = await testClient.getBlockNumber();
      
      console.log("blockNumber = " + blockNumber);

      const currentPage = 0;

      const startingBlock = blockNumber - BigInt(currentPage * BLOCKS_PER_PAGE);
      
      const blockNumbersToFetch = Array.from(
        { length: Number(BLOCKS_PER_PAGE < startingBlock + 1n ? BLOCKS_PER_PAGE : startingBlock + 1n) },
        (_, i) => startingBlock - BigInt(i),
      );
      console.log("blockNumbersToFetch = " + blockNumbersToFetch);
      const blocksWithTransactions = blockNumbersToFetch.map(async blockNumber => {
        try {
          return testClient.getBlock({ blockNumber, includeTransactions: true });
        } catch (err) {
          console.log(err instanceof Error ? err : new Error("An error occurred."));
          throw err;
        }
      });
 
      const fetchedBlocks = await Promise.all(blocksWithTransactions);
      console.log(fetchedBlocks)

      fetchedBlocks.forEach(block => {
        block.transactions.forEach(tx => decodeTransactionData(tx as Transaction));
      });

      const txReceipts = await Promise.all(
        fetchedBlocks.flatMap(block =>
          block.transactions.map(async tx => {
            try {
              const receipt = await testClient.getTransactionReceipt({ hash: (tx as Transaction).hash });
              //console.log(receipt)
            } catch (err) {
              console.log(err instanceof Error ? err : new Error("An error occurred."));
              throw err;
            }
          }),
        ),
      );

    } catch (err) {
      console.log(err instanceof Error ? err : new Error("An error occurred."));
    }